/*
 * @brief Sensor Hub demo application (implementation file)
 *
 * @note
 * Copyright(C) NXP Semiconductors, 2015
 * All rights reserved.
 *
 * @par
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * LPC products.  This software is supplied "AS IS" without any warranties of
 * any kind, and NXP Semiconductors and its licensor disclaim any and
 * all warranties, express or implied, including all implied warranties of
 * merchantability, fitness for a particular purpose and non-infringement of
 * intellectual property rights.  NXP Semiconductors assumes no responsibility
 * or liability for the use of the software, conveys no license or rights under any
 * patent, copyright, mask work right, or any other intellectual property rights in
 * or to any products. NXP Semiconductors reserves the right to make changes
 * in the software without notification. NXP Semiconductors also makes no
 * representation or warranty that such application will be suitable for the
 * specified use without further testing or modification.
 *
 * @par
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, under NXP Semiconductors' and its
 * licensor's relevant copyrights in the software, without fee, provided that it
 * is used in conjunction with NXP Semiconductors microcontrollers.  This
 * copyright, permission, and disclaimer notice must appear in all copies of
 * this code.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <time.h>

#include <string.h>
#include <popt.h>

#include "trace.h"

#include "sensors.h"
#include "sensor_pkt.h"
#include "hostif_protocol.h"

#define OPT_ABOUT			0x0001
#define OPT_TRACE			0x0002
#define OPT_RAW				0x0004
#define OPT_INPUT			0x0010
#define OPT_OUTPUT			0x0020

#define RX_BUFF_CT			512

//------------------------------------------------------------------------------
// Static data / functions
//------------------------------------------------------------------------------
static poptContext	optCon;
static FILE_HDR_T	file_hdr;
static BLOCK_HDR_T	block_hdr;
static uint8_t		block_data[RX_BUFF_CT];

static void		about(void);
static bool		read_file_hdr(FILE* f, FILE_HDR_T* h);
static bool		read_data_hdr(FILE* f, BLOCK_HDR_T* h);
static bool		read_sensor_data(FILE* f, uint8_t* buff, uint32_t ct);
static bool 	parse_sensor_data(uint8_t* pData, uint16_t datalen);

//------------------------------------------------------------------------------
// Main function
//------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
	uint32_t	opt=0;															/* options used with popt library */
	int			idx=0;															/* idx is used with the popt library */
	int			trace_level = 0x00;												/* All=0x0f, INFO=0x04, DEBUG=0x02, CRITICAL=0x01, NONE=0x00 */
	char*		i_file	= NULL;													/* input file name */
	char*		o_file	= NULL;													/* output file name */
	FILE*		fh_in	= NULL;													/* input file handle */
	FILE*		fh_out	= NULL;													/* output file handle */

	struct poptOption optionsTable[] = {
		{ "about",		'a', POPT_ARG_NONE,		NULL,			'a', "Display information about the app.",						NULL	},
		{ "trace",		't', POPT_ARG_INT,		&trace_level,	't', "Trace bits: critical=1, debug=2, info=4, enter/exit=8",	NULL	},
		{ "raw",		'r', POPT_ARG_NONE,		NULL,			'r', "File output format: CSV, raw.",							NULL	},
		{ "input",		'i', POPT_ARG_STRING,	&i_file,		'i', "input=filename.bin: Input filename",						NULL	},
		{ "output",		'o', POPT_ARG_STRING,	&o_file,		'o', "output=filename.csv: Output filename",					NULL	},
		POPT_AUTOHELP
		POPT_TABLEEND
	};

//------------------------------------------------------------------------------
//  No buffering for Eclipse/CDT (bug 173732)
//
//	This won't harm anything but it's not required
//	for normal console operation.
//
//	Remove if you aren't using Eclipse/CDT.
//------------------------------------------------------------------------------
//	setvbuf(stdout, NULL, _IONBF, 0);
//	setvbuf(stdin, NULL, _IONBF, 0);

//------------------------------------------------------------------------------
//  Process arguments
//------------------------------------------------------------------------------
	optCon = poptGetContext(NULL, argc, (const char **)argv, optionsTable, 0);
	poptSetOtherOptionHelp(optCon, "[OPTIONS]*");
	while ((idx = poptGetNextOpt(optCon)) >= 0) {
		switch (idx) {
		case 'a':
			opt |= OPT_ABOUT;
			break;
		case 't':
			opt |= OPT_TRACE;
			break;
		case 'r':
			opt |= OPT_RAW;
			break;
		case 'i':
			opt |= OPT_INPUT;
			break;
		case 'o':
			opt |= OPT_OUTPUT;
			break;
		}
	}
	if (idx < -1) {
		fprintf(stderr, "%s: %s\n",
		        poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
		        poptStrerror(idx));
		exit(opt);
	}
	if ((opt & OPT_INPUT) == 0) {
		printf("Input file required\n");
		exit(0);
	}
	if ((opt & OPT_OUTPUT) == 0) {
		printf("Output file required\n");
		exit(0);
	}
	if (opt & OPT_ABOUT) {
		about();
		exit(0);
	}
	if (opt & OPT_TRACE) {
		trace_init(trace_level);
	} else {
		trace_init(0);
	}


//------------------------------------------------------------------------------
//  Application code
//------------------------------------------------------------------------------
	fh_in	= fopen(i_file, "rb");												// open input file
	fh_out	= fopen(o_file, "w");												// open output file

	if (fh_in == NULL) {														// if open fails...
		printf("cannot open input file: %s\n",i_file);							// log it
		exit(1);																// exit
	}

	if (fh_out == NULL) {														// if open fails...
		printf("cannot open output file: %s\n",o_file);							// log it
		exit(1);																// exit
	}
	set_file(fh_out);

	read_file_hdr(fh_in, &file_hdr);
	while (read_data_hdr(fh_in, &block_hdr) == true) {
		read_sensor_data(fh_in, block_data, block_hdr.size);
		if (opt & OPT_RAW)
			dump_data(block_data, block_hdr.size);
		else
			parse_sensor_data(block_data, block_hdr.size);
	}

	fclose(fh_out);																// close output
	fclose(fh_in);																// close input file
}

//------------------------------------------------------------------------------
//	about
//
//	Display "about" information.
//------------------------------------------------------------------------------
static void about(void)
{
	printf("Sensor Play:\n");
	printf("Copyright (C) NXP Semiconductors, 2015\n");
	printf("All rights reserved.\n");
	printf("version: 0.1.1.0\n");
}

//------------------------------------------------------------------------------
//	read file header
//
//	This function reads and decodes the file header
//
//------------------------------------------------------------------------------
static bool read_file_hdr(FILE* f, FILE_HDR_T* h)
{
	size_t	read_sz;
	char	hdr_id[8];
	char*	c_time_string;

	read_sz = fread(h, file_hdr_sz, 1, f);										/* read the file header */
	assert(read_sz == 1);														/* verify read */

	memset(hdr_id, 0, 8);														/* clear the string */
	memcpy(hdr_id, &h->id, 4);													/* copy in file type */

	trace(TRACE_INFO, "header tag: %4s", hdr_id);								/* trace file header information */
	trace(TRACE_INFO, "header version: %08x", h->ver);
    c_time_string = ctime(&h->time);
	c_time_string[strlen(c_time_string)-1]=0;
	trace(TRACE_INFO, "recording date/time: %s", c_time_string);

	return true;
}

//------------------------------------------------------------------------------
//	read block header
//
//	This function reads and decodes a block header
//
//------------------------------------------------------------------------------
static bool read_data_hdr(FILE* f, BLOCK_HDR_T* h)
{
	size_t	read_sz;

	read_sz = fread(h, blk_hdr_sz, 1, f);										/* read the header */
	assert(read_sz == 1);														/* verify read */

	trace(TRACE_INFO, "block tag: %8x", h->tag);								/* trace the header information */
	trace(TRACE_INFO, "block id: %d", h->id);
	trace(TRACE_INFO, "block size: %d", h->size);

	return (h->tag == BLK_HDR_TAG_DATA) ? true : false;							/* return the header type */
}

//------------------------------------------------------------------------------
//	get sensor data
//
//	This function gets sensor data from a binary log file
//------------------------------------------------------------------------------
static bool read_sensor_data(FILE* f, uint8_t* buff, uint32_t pkt_sz)
{
	size_t	read_sz;

	memset(buff, 0, RX_BUFF_CT);												/* clear the block */
	read_sz = fread(buff, 1, pkt_sz, f);										/* get the data */
	assert(read_sz == pkt_sz);													/* verify read */

	return true;
}

//------------------------------------------------------------------------------
//	parse sensor data
//
//	This function parses the sensor data 
//------------------------------------------------------------------------------
static bool parse_sensor_data(uint8_t* pData, uint16_t datalen)
{
	uint8_t sensor_id;
	uint32_t offset=0;
	uint32_t pkt_len;

	while (datalen > offset) {
		sensor_id = *pData;
		switch (sensor_id) {
		case LPCSH_SENSOR_ID_ACCELEROMETER:
			accel_cal_init();
			accel_cal_acq(pData);
			accel_cal_print();
			break;
	
		case LPCSH_SENSOR_ID_ACCELEROMETER_UNCALIBRATED:
			accel_uncal_init();
			accel_uncal_acq(pData);
			accel_uncal_print();
			break;
	
		case LPCSH_SENSOR_ID_MAGNETIC_FIELD:
			mag_cal_init();
			mag_cal_acq(pData);
			mag_cal_print();
			break;
	
		case LPCSH_SENSOR_ID_MAGNETIC_FIELD_UNCALIBRATED:
			mag_uncal_init();
			mag_uncal_acq(pData);
			mag_uncal_print();
			break;
	
		case LPCSH_SENSOR_ID_GYROSCOPE:
			gyro_cal_init();
			gyro_cal_acq(pData);
			gyro_cal_print();
			break;
	
		case LPCSH_SENSOR_ID_GYROSCOPE_UNCALIBRATED:
			gyro_uncal_init();
			gyro_uncal_acq(pData);
			gyro_uncal_print();
			break;
	
		case LPCSH_SENSOR_ID_LIGHT:
			light_init();
			light_acq(pData);
			light_print();
			break;
	
		case LPCSH_SENSOR_ID_PRESSURE:
			pressure_init();
			pressure_acq(pData);
			pressure_print();
			break;
	
		case LPCSH_SENSOR_ID_PROXIMITY:
			proximity_init();
			proximity_acq(pData);
			proximity_print();
			break;
	
		case LPCSH_SENSOR_ID_ORIENTATION:
			orientation_init();
			orientation_acq(pData);
			orientation_print();
			break;
	
		case LPCSH_SENSOR_ID_ROTATION_VECTOR:
			rotation_init();
			rotation_acq(pData);
			rotation_print();
			break;
	
		case LPCSH_SENSOR_ID_FAKE_SENSOR:
			fake_init();
			fake_acq(pData);
			fake_print();
			break;
			
		default:
			puts("unknown sensor");
			exit(1);
			break;
		}

		if (sensor_id == LPCSH_SENSOR_ID_FAKE_SENSOR) {
			pData += 1;
			offset += 1;
		}
		else {
			pkt_len = get_hdr_len() + get_data_len(sensor_id);
			pData += pkt_len;
			offset += pkt_len;
		}
	}

	trace(TRACE_INFO, "datalen:%d offset:%d", datalen, offset);
	return true;
}

//
//	End: sensor_play.c
//
